package com.minivision.fdiot.mqtt.core;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

import com.minivision.fdiot.mqtt.core.MqttMessageHandler.MqttMessageBody;
import com.minivision.fdiot.mqtt.core.MqttMessageHandler.MqttMessageParam;
import com.minivision.fdiot.protopool.Packet;
import com.minivision.fdiot.protopool.PacketUtils;
import com.minivision.fdiot.protopool.Packet.Head;
import com.minivision.fdiot.protopool.Packet.Head.Type;

import lombok.extern.slf4j.Slf4j;

/**
 * 指令消息处理器上下文
 * @author hughzhao
 * @2018年3月1日
 */
@Slf4j
public class MsgHandlerMethodContext {
  
  private Object instance;
  private Method method;
  
  public MsgHandlerMethodContext(Object instance, Method method) {
    this.instance = instance;
    this.method = method;
  }
  
  public Packet<?> process(String clientId, String username, Head head, Object bodyWrapper) throws Exception {
    Parameter[] ps = method.getParameters();
    List<Object> args = new ArrayList<>();
    for(Parameter p : ps){
      Object arg = searchArg(p, clientId, username, head, bodyWrapper);
      args.add(arg);
    }
    
    Object o = method.invoke(instance, args.toArray());
    log.trace("invoke message process method, class:{}, method:{}, data:{}", instance.getClass().getName(), method.getClass().getName(), args);
    
    if (o == null) {
        //if (method.getReturnType() == void.class || method.getReturnType() == Void.class) //should be notify message
        //return null;
        o = Type.RESPONSE_OK;// default set return type to ok
    }

    Packet<?> rs;
    if (o instanceof Integer) {// type
        rs = new Packet<Object>(PacketUtils.getInstance().buildResponseHead(head, (int) o));
    } else if (o instanceof Head) {// head
        rs = new Packet<Object>((Head) o);
    } else if (o instanceof Packet) {
        rs = (Packet<?>) o;
    } else {// should be Packet body
        rs = new Packet<Object>(PacketUtils.getInstance().buildResponseHead(head, Type.RESPONSE_OK), o);
    }
    return rs;
  }
  
  private Object searchArg(Parameter p, String clientId, String username, Head head, Object body){
    if(p.getType().equals(Head.class)){
      return head;
    }
    if(p.getType().equals(String.class)){
      MqttMessageParam annotation = p.getDeclaredAnnotation(MqttMessageParam.class);
      switch(annotation.value()){
        case clientId:
          return clientId; 
        case username:
          return username;
      }
    }
    
    try {
      return PacketUtils.getInstance().parseBody(body, p.getType());
    } catch (Exception e) {
      log.error("parse body error", e);
      if(p.isAnnotationPresent(MqttMessageBody.class)){
        throw new RuntimeException("body deserialize error", e);
      }
    }
    return null;
  }

}
